﻿using Core.Framework;
using DotNetty.Transport.Channels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace DotNetty.Framework.BaseTriggers
{


    public class WorkerValue
    {
        public WorkerController Worker { get; set; }
        public MethodInfo Method { get; set; }

        /// <summary>
        /// 是否需要解析参数
        /// </summary>
        public bool IsHavParameter { get; set; }
        public bool IsAsync { get; set; }
    }

    public abstract class AbstractCommandTrigger
    {

        /// <summary>
        /// 获取所有的Route对应的方法
        /// </summary>
        static Dictionary<string, WorkerValue> RouteDictionary = new Dictionary<string, WorkerValue>();


        static AbstractCommandTrigger()
        {
            INIIChannelWorker();
        }

        static void INIIChannelWorker()
        {
            //var types = AppDomain.CurrentDomain.GetAssemblies()
            //            .SelectMany(a => a.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IChannelWorker))))
            //            .ToArray();

            Type[] types = AppDomain.CurrentDomain.GetAssemblies()
                        .SelectMany(a => a.GetTypes().Where(t => t.IsSubclassOf(typeof(WorkerController))))
                        .ToArray();


            foreach (var item in types)
            {
                WorkerController worker = CreateInstanceByType(item);
                WorkerAttribute attribute = item.GetCustomAttribute<WorkerAttribute>();

                string routeFrist = string.Empty;
                if (attribute != null)
                {
                    routeFrist = attribute.Route;
                }

                MethodInfo[] methods = item.GetMethods();
                foreach (var method in methods)
                {
                    if (!method.IsPublic)
                        continue;

                    var routeInfo = new WorkerValue();
                    MethodRouteAttribute attributeFromMethod = method.GetCustomAttribute<MethodRouteAttribute>();

                    if (attributeFromMethod == null)
                        continue;

                    routeInfo.IsHavParameter = attributeFromMethod.IsHavParameter;

                    routeInfo.Worker = worker;
                    routeInfo.Method = method;
                    routeInfo.IsAsync = method.GetCustomAttribute<AsyncStateMachineAttribute>() != null; // 判断是否是异步的方法
                    RouteDictionary.Add(
                        attributeFromMethod.IgnoreOtherRoute ? attributeFromMethod.Route : routeFrist + attributeFromMethod.Route,
                        routeInfo);
                }
            }
        }

        /// <summary>
        /// 根据类型创建实例
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        static WorkerController CreateInstanceByType(Type item)
        {
            Type[] constractorParameterTypes = new Type[0];
            object[] parametes = new object[0];
            ParameterInfo[] constractorParameter = item.GetConstructors().Single().GetParameters();
            if (constractorParameter.Count() > 0)
            {
                constractorParameterTypes = constractorParameter.Select(a => a.ParameterType).ToArray();
                parametes = new object[constractorParameterTypes.Length];
                for (int i = 0; i < constractorParameterTypes.Count(); i++)
                {
                    parametes[i] = ProviderOfServices.GetService(constractorParameterTypes[i]);
                }
            }

            //if ((item as TypeInfo).GenericTypeParameters.Length > 0)
            //{
            //    item = item.MakeGenericType(typeof(TMessage));
            //}

            return (WorkerController)Activator.CreateInstance(item, parametes);
        }

        /// <summary>
        /// 获取Route对应的Worker信息
        /// </summary>
        /// <returns></returns>
        public WorkerValue GetCommandWorker(AbstractMessage context, out string value)
        {
            value = string.Empty;

            if (context == null || string.IsNullOrWhiteSpace(context.CommandText))
                return default;

            string route = this.TrimAndToLower(context.CommandText);
            // 尝试匹配路由
            if (RouteDictionary.ContainsKey(route))
            {
                return RouteDictionary[route];
            }
            else
            {
                // 检查是否带Value的路由
                var parameter = this.GetCommandInfo(context.CommandText, route);
                if (RouteDictionary.ContainsKey(parameter.Item1))
                {
                    // 就算匹配到如果，特性是不带参数的。 说明结果不对
                    var workerInfo = RouteDictionary[parameter.Item1];
                    if (workerInfo.IsHavParameter)
                    {
                        value = parameter.Item2;
                        return workerInfo;
                    }
                }
            }

            return default;
        }

        /// <summary>
        /// 小写去除空格
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private string TrimAndToLower(string value)
        {
            return value.ToLower().Replace(" ", "");
        }

        /// <summary>
        /// 获取路由和参数信息
        /// </summary>
        /// <param name="value"></param>
        /// <param name="routeFullLower"></param>
        /// <returns></returns>
        private (string, string) GetCommandInfo(string value, string routeFullLower)
        {
            string[] commandArray = value.Split(" ");

            // 最后一个值
            string lastValue = commandArray.Last();
            string route = routeFullLower.Replace(lastValue.ToLower(), string.Empty);

            return (route, lastValue);
        }

        /// <summary>
        /// 构造参数上下文信息
        /// </summary>
        /// <returns></returns>
        protected HandleDoWorkingContext BuildContext(IChannelHandlerContext context, AbstractMessage message)
        {
            message.TaskStartDateTime = DateTime.Now;
            return new HandleDoWorkingContext
            {
                Message = message,
                ChannelHandlerContext = context,
                Channel = context.GetChannel()
            };
        }

        protected async Task<DoWorkResponse> InvokeMethod(WorkerValue channelWorker, object[] parameter)
        {
            if (channelWorker.IsAsync)
            {
                return await ((Task<DoWorkResponse>)channelWorker
                    .Method
                    //.MakeGenericMethod(new Type[] { typeof(TMessage) })
                    .Invoke(channelWorker.Worker, parameter))
                    .ConfigureAwait(false);
            }
            else
            {
                return (DoWorkResponse)channelWorker
                    .Method
                    //.MakeGenericMethod(new Type[] { typeof(TMessage) })
                    .Invoke(channelWorker.Worker, parameter);
            }
        }

    }
}
